2 * Adium is the legal property of its developers, whose names are listed in the copyright file included
3 * with this source distribution.
5 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
6 * General Public License as published by the Free Software Foundation; either version 2 of the License,
7 * or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
10 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
11 * Public License for more details.
13 * You should have received a copy of the GNU General Public License along with this program; if not,
14 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 #import "adiumGaimEventloop.h"
18 #import <AIUtilities/CBApplicationAdditions.h>
20 static guint sourceId = nil; //The next source key; continuously incrementing
21 static NSMutableDictionary *sourceInfoDict = nil;
22 static CFRunLoopRef gaimRunLoop = nil;
24 static void socketCallback(CFSocketRef s,
25 CFSocketCallBackType callbackType,
30 * The sources, keyed by integer key id (wrapped in an NSValue), holding
31 * struct sourceInfo* values (wrapped in an NSValue).
34 // The structure of values of sourceInfoDict
37 CFRunLoopTimerRef timer;
39 CFRunLoopSourceRef rls;
41 GSourceFunc sourceFunction;
42 GaimInputFunction ioFunction;
50 guint adium_source_remove(guint tag) {
51 struct SourceInfo *sourceInfo = (struct SourceInfo*)
52 [[sourceInfoDict objectForKey:[NSNumber numberWithUnsignedInt:tag]] pointerValue];
54 // GaimDebug (@"***SOURCE REMOVE : %i",tag);
56 if (sourceInfo->timer != NULL) {
57 //Got a timer; invalidate and release
58 CFRunLoopTimerInvalidate(sourceInfo->timer);
59 CFRelease(sourceInfo->timer);
62 //Got a file handle; invalidate and release the source and the socket
63 CFRunLoopSourceInvalidate(sourceInfo->rls);
64 CFRelease(sourceInfo->rls);
65 CFSocketInvalidate(sourceInfo->socket);
66 CFRelease(sourceInfo->socket);
69 [sourceInfoDict removeObjectForKey:[NSNumber numberWithUnsignedInt:tag]];
78 //Like g_source_remove, return TRUE if successful, FALSE if not
79 guint adium_timeout_remove(guint tag) {
80 return (adium_source_remove(tag));
85 void callTimerFunc(CFRunLoopTimerRef timer, void *info)
87 struct SourceInfo *sourceInfo = info;
89 // GaimDebug (@"%x: Fired %f-ms timer (tag %u)",[NSRunLoop currentRunLoop],CFRunLoopTimerGetInterval(timer)*1000,sourceInfo->tag);
90 if (! sourceInfo->sourceFunction(sourceInfo->user_data)) {
91 adium_source_remove(sourceInfo->tag);
95 guint adium_timeout_add(guint interval, GSourceFunc function, gpointer data)
97 // GaimDebug (@"%x: New %u-ms timer (tag %u)",[NSRunLoop currentRunLoop], interval, sourceId);
99 struct SourceInfo *info = (struct SourceInfo*)malloc(sizeof(struct SourceInfo));
102 NSTimeInterval intervalInSec = (NSTimeInterval)interval/1000;
103 CFRunLoopTimerContext runLoopTimerContext = { 0, info, NULL, NULL, NULL };
104 CFRunLoopTimerRef runLoopTimer = CFRunLoopTimerCreate(kCFAllocatorDefault, /* default allocator */
105 (CFAbsoluteTimeGetCurrent() + intervalInSec), /* The time at which the timer should first fire */
106 intervalInSec, /* firing interval */
107 0, /* flags, currently ignored */
108 0, /* order, currently ignored */
109 callTimerFunc, /* CFRunLoopTimerCallBack callout */
110 &runLoopTimerContext /* context */
113 info->sourceFunction = function;
114 info->timer = runLoopTimer;
117 info->user_data = data;
119 CFRunLoopAddTimer(gaimRunLoop, runLoopTimer, kCFRunLoopCommonModes);
121 NSNumber *key = [NSNumber numberWithUnsignedInt:sourceId];
122 //Make sure we end up with a valid source id
123 while ([sourceInfoDict objectForKey:key]){
125 key = [NSNumber numberWithUnsignedInt:sourceId];
127 info->tag = sourceId;
129 [sourceInfoDict setObject:[NSValue valueWithPointer:info]
135 guint adium_input_add(int fd, GaimInputCondition condition,
136 GaimInputFunction func, gpointer user_data)
138 struct SourceInfo *info = (struct SourceInfo*)malloc(sizeof(struct SourceInfo));
140 // Build the CFSocket-style callback flags to use from the gaim ones
141 CFOptionFlags callBackTypes = 0;
142 if ((condition & GAIM_INPUT_READ ) != 0) callBackTypes |= kCFSocketReadCallBack;
143 if ((condition & GAIM_INPUT_WRITE) != 0) callBackTypes |= kCFSocketWriteCallBack;
144 // if ((condition & GAIM_INPUT_CONNECT) != 0) callBackTypes |= kCFSocketConnectCallBack;
146 // And likewise the entire CFSocket
147 CFSocketContext context = { 0, info, NULL, NULL, NULL };
148 CFSocketRef socket = CFSocketCreateWithNative(NULL, fd, callBackTypes, socketCallback, &context);
149 NSCAssert(socket != NULL, @"CFSocket creation failed");
150 info->socket = socket;
152 // Re-enable callbacks automatically and _don't_ close the socket on
154 CFSocketSetSocketFlags(socket, kCFSocketAutomaticallyReenableReadCallBack |
155 kCFSocketAutomaticallyReenableDataCallBack |
156 kCFSocketAutomaticallyReenableWriteCallBack);
158 // Add it to our run loop
159 CFRunLoopSourceRef rls = CFSocketCreateRunLoopSource(NULL, socket, 0);
161 CFRunLoopAddSource(gaimRunLoop, rls, kCFRunLoopCommonModes);
165 // GaimDebug (@"Adding for %i",sourceId);
169 info->tag = sourceId;
170 info->ioFunction = func;
171 info->user_data = user_data;
173 NSCAssert1([sourceInfoDict objectForKey:[NSNumber numberWithUnsignedInt:sourceId]] == nil, @"Key %u in use", sourceId);
174 [sourceInfoDict setObject:[NSValue valueWithPointer:info]
175 forKey:[NSNumber numberWithUnsignedInt:sourceId]];
180 #pragma mark Socket Callback
181 static void socketCallback(CFSocketRef s,
182 CFSocketCallBackType callbackType,
187 struct SourceInfo *sourceInfo = (struct SourceInfo*) infoVoid;
189 GaimInputCondition c = 0;
190 if ((callbackType & kCFSocketReadCallBack) != 0) c |= GAIM_INPUT_READ;
191 if ((callbackType & kCFSocketWriteCallBack) != 0) c |= GAIM_INPUT_WRITE;
192 // if ((callbackType & kCFSocketConnectCallBack) != 0) c |= GAIM_INPUT_CONNECT;
194 // GaimDebug (@"***SOCKETCALLBACK : %i (%i)",info->fd,c);
196 if ((callbackType & kCFSocketConnectCallBack) != 0) {
197 //Got a file handle; invalidate and release the source and the socket
198 CFRunLoopSourceInvalidate(sourceInfo->rls);
199 CFRelease(sourceInfo->rls);
200 CFSocketInvalidate(sourceInfo->socket);
201 CFRelease(sourceInfo->socket);
203 [sourceInfoDict removeObjectForKey:[NSNumber numberWithUnsignedInt:sourceInfo->tag]];
204 sourceInfo->ioFunction(sourceInfo->user_data, sourceInfo->fd, c);
208 // GaimDebug (@"%x: Socket callback: %i",[NSRunLoop currentRunLoop],sourceInfo->tag);
209 sourceInfo->ioFunction(sourceInfo->user_data, sourceInfo->fd, c);
214 static GaimEventLoopUiOps adiumEventLoopUiOps = {
216 adium_timeout_remove,
221 GaimEventLoopUiOps *adium_gaim_eventloop_get_ui_ops(void)
223 if(!sourceInfoDict) sourceInfoDict = [[NSMutableDictionary alloc] init];
225 //Determine our run loop
226 gaimRunLoop = CFRunLoopGetCurrent();
227 CFRetain(gaimRunLoop);
229 return &adiumEventLoopUiOps;